import requests
from bs4 import BeautifulSoup
import time
import pymongo
import random

"""
        UA伪装，反反爬取策略
"""
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
    'Cookie': 'll="118316"; bid=hM572t0EZQ4; apiKey=; __utma=30149280.1350320709.1669219899.1708177624.1710769797.7; __utmc=30149280; __utmz=30149280.1710769797.7.4.utmcsr=bing|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided); __utmt=1; vtoken=undefined; last_login_way=phone; push_noty_num=0; push_doumail_num=0; __utmv=30149280.26367; user_data={%22area_code%22:%22+86%22%2C%22number%22:%2218756008184%22%2C%22code%22:%229572%22}; __utmb=30149280.24.9.1710770168237; login_start_time=1710770170476'
}

"""
        下面三行代码用于数据库链接，每一行代码都已经做好注释
"""

"""
这一行代码创建了一个MongoDB客户端，尝试连接到在本地机器（localhost）上运行的MongoDB服务器，
使用的端口号是27017（这是MongoDB的默认端口）。
MongoClient是pymongo库中的一个类，用于与MongoDB服务器建立连接。
连接成功后，你可以通过这个db对象来访问和操作MongoDB数据库。
"""
db = pymongo.MongoClient('localhost',27017)

"""
这一行代码选择了名为douban的数据库。如果这个数据库在MongoDB服务器上不存在，MongoDB会在第一次向其中插入数据时创建它。
db['douban']是通过数据库名称（在这里是douban）从MongoDB服务器中选择数据库的方法。
douban_db现在是一个指向douban数据库的对象，你可以用它来执行数据库操作，如读取、插入、更新或删除数据。
"""
# douban_db = db['douban']
douban_db = db['doubantop250']

"""
这一行代码选择了douban数据库中的一个名为top250的集合（在MongoDB中，一个集合类似于SQL数据库中的一个表）。
如果这个集合不存在，MongoDB会在第一次向其中插入数据时创建它。
douban_db['top250']是通过集合名称（在这里是top250）从douban数据库中选择集合的方法。
movie_data现在是一个指向top250集合的对象，你可以用它来执行集合级别的操作，
如查询、插入、更新或删除文档（在MongoDB中，一个文档类似于SQL数据库中的一行记录）。
"""
movie_data = douban_db['movietop250']


'''
        抓取top250电影的：名字，上映时间，上映地区，类型，评分，评论人数，导演，“精评”
'''
def get_movie_data(url, headers):
    """
    创建一个函数用于爬虫
    :param url: 爬取网站的地址
    :param headers: 用于伪装自己
    :return: 没有返回值
    """
    # 通过查看network抓取的数据包，发现请求方法是get
    resp = requests.get(url, headers=headers)
    # 使用bs4进行解析，'html.parser': 这是指定给 BeautifulSoup 使用的解析器类型。html.parser 是 Python 标准库中的一个 HTML 解析器。
    soup = BeautifulSoup(resp.text, 'html.parser')
    # 使用CSS选择器进行提取内容，选取div标签下的class="info"的标签
    list_old = soup.select('div.info')
    # 提取的内容不止一个，下面使用for循环遍历
    for list_new in list_old:
        # 使用CSS选择器进行提取内容，选取div标签下的class="hd"的标签下的class="title"的标签，下面同理
        if list_new.select('div.hd .title'):
            # select方法返回的是一个列表，列表可能是空列表，提前用if结构进行预判
            name = list_new.select('div.hd .title')[0].text # 获取电影名字
        else:
            name = "未找到电影名字"

        # 以第一个为例，下面演示数据清洗
        # 导演: 弗兰克·德拉邦特 Frank Darabont &nbsp;&nbsp;&nbsp;主演: 蒂姆·罗宾斯 Tim Robbins /...<br>1994 &nbsp;/&nbsp;美国 &nbsp;/&nbsp;犯罪 剧情
        all_info = list_new.select('div.bd p')[0].text

        # 前半段包含人物的信息，导演: 弗兰克·德拉邦特 Frank Darabont &nbsp;&nbsp;&nbsp;主演: 蒂姆·罗宾斯 Tim Robbins /
        person_list = all_info.strip('').split('...')[0]

        # 提取导演数据
        ls_director = person_list.split("&nbsp;&nbsp;&nbsp;")
        director = ls_director[0].strip().split(": ")[1] # 获得电影导演
        director = director.split("   ")[0] # 处理数据

        # 后半段信息包含电影上映时间，上映地区，电影类型 <br>1994 &nbsp;/&nbsp;美国 &nbsp;/&nbsp;犯罪 剧情
        type_list = all_info.strip('').split('...')[-1].replace(' ', '').split('/')

        year = type_list[0].replace("&nbsp;", "").replace("<br>", "") # 电影发行年份
        country = type_list[1].replace("&nbsp;", "") # 电影发行国家
        category = type_list[2].replace("&nbsp;", "") # 电影分类

        # select方法返回的是一个列表，列表可能是空列表，所以需要进行预判，下面同理
        if list_new.select('div.bd .star .rating_num'):
            score = list_new.select('div.bd .star .rating_num')[0].text.replace(' ', '') # 电影评分
        else:
            score = "未找到评分信息"

        # -------------------------------------------- 新增
        # quote =list.select('div.bd .quote')[0].text
        if list_new.select('div.bd .quote'):
            quote = list_new.select('div.bd .quote')[0].text.replace(' ', '') # 电影引言
        else:
            quote = "未找到电影引言"
        # --------------------------------------------

        if list_new.select('div.bd .star span:nth-of-type(4)'):
            people_comment_num = list_new.select('div.bd .star span:nth-of-type(4)')[0].text.split('人')[0] # 电影评论人数
        else:
            people_comment_num = "未找到电影评论人数"

        # ---------------------------------     新增
        # year = int(year)
        # score = float(score)
        # people_comment_num = int(people_comment_num)
        # ------------------------------------

        data = {
            'name': name,
            'year': year,
            'country': country,
            'category': category,
            'score': score,
            'people_num': people_comment_num,
            'director': director,
            'quote': quote
        }

        print(data)
        # 将爬取来的数据插入到数据库中
        movie_data.insert_one(data)



'''
        构建递归循环；逐页爬取；
'''
if __name__ =='__main__':
    for i in range(0, 10):
        num = str(i * 25)
        # 不用额外编码，返回的页面源代码中的中文是正常的
        url = f'https://movie.douban.com/top250?start={num}&filter='
        t = random.randint(5, 10) # 随机一个睡眠时间，减少服务器压力
        time.sleep(t)
        get_movie_data(url, headers)